#include "redis_client.h"

namespace redisSpace {
    RedisClient::RedisClient() = default;
    RedisClient::RedisClient(const std::string& host, int port, std::string password, int index, int timeout, int poolSize) {
        this->Init(host, port, password, index, timeout, poolSize);
    }
    RedisClient::~RedisClient() = default;

    bool RedisClient::Init(const std::string& host, int port, std::string password, int index, int timeout, int poolSize) {
        try {
           pool_ = std::make_unique<RedisConnectionPool>(host, port, password, index, timeout, poolSize);            
           return true;
        } catch (...) {
           return false;
        }
    }

    // Key commands implementation
    bool RedisClient::Select(int index) {
		if (index < 0) {
			index = -index;
		}
        index %= 15;
        
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->select(index).first;
    }

    bool RedisClient::Exists(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->exists(key).first;
    }

    bool RedisClient::Expire(const std::string& key, int seconds) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->expire(key, seconds).first;
    }

    bool RedisClient::PExpire(const std::string& key, int64_t milliseconds) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->pexpire(key, milliseconds).first;
    }

    int64_t RedisClient::TTL(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->ttl(key).second;
    }

    int64_t RedisClient::PTTL(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->pttl(key).second;
    }

    bool RedisClient::Persist(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->persist(key).first;
    }

    redisOPResult RedisClient::Keys(const std::string& pattern) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->keys(pattern);
    }

    bool RedisClient::Rename(const std::string& key, const std::string& newKey) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->rename(key, newKey).first;
    }

    std::string RedisClient::Type(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->type(key).second;
    }

    // String commands implementation
    redisOPResult RedisClient::Get(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->get(key);
    }

    bool RedisClient::SetNX(const std::string& key, const std::string& value) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->setnx(key, value).first;
    }

    bool RedisClient::SetEX(const std::string& key, int seconds, const std::string& value) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->setex(key, seconds, value).first;
    }

    bool RedisClient::PSetEX(const std::string& key, int64_t milliseconds, const std::string& value) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->psetex(key, milliseconds, value).first;
    }

    bool RedisClient::MSet(const std::map<std::string, std::string>& kv_pairs) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->mset(kv_pairs).first;
    }

    redisOPResult RedisClient::MGet(const std::vector<std::string>& keys) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->mget(keys);
    }

    int64_t RedisClient::Incr(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->incr(key).second;
    }

    int64_t RedisClient::Decr(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->decr(key).second;
    }

    int64_t RedisClient::IncrBy(const std::string& key, int64_t increment) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->incrby(key, increment).second;
    }

    int64_t RedisClient::DecrBy(const std::string& key, int64_t decrement) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->decrby(key, decrement).second;
    }

    double RedisClient::IncrByFloat(const std::string& key, double increment) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->incrbyfloat(key, increment).second;
    }

    int RedisClient::Append(const std::string& key, const std::string& value) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->append(key, value).second;
    }

    int RedisClient::StrLen(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->strlen(key).second;
    }

    // Hash commands implementation
    redisOPResult RedisClient::HGet(const std::string& key, const std::string& field) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hget(key, field);
    }

    bool RedisClient::HDel(const std::string& key, const std::string& field) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hdel(key, field).first;
    }

    redisOPResult RedisClient::HGetAll(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hgetall(key);
    }

    bool RedisClient::HMSet(const std::string& key, const std::map<std::string, std::string>& fieldValues) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hmset(key, fieldValues).first;
    }

    redisOPResult RedisClient::HMGet(const std::string& key, const std::vector<std::string>& fields) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hmget(key, fields);
    }

    int RedisClient::HLen(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hlen(key).second;
    }

    redisOPResult RedisClient::HKeys(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hkeys(key);
    }

    redisOPResult RedisClient::HVals(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hvals(key);
    }

    bool RedisClient::HExists(const std::string& key, const std::string& field) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hexists(key, field).second;
    }

    int64_t RedisClient::HIncrBy(const std::string& key, const std::string& field, int64_t increment) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hincrby(key, field, increment).second;
    }

    double RedisClient::HIncrByFloat(const std::string& key, const std::string& field, double increment) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hincrbyfloat(key, field, increment).second;
    }

    bool RedisClient::HSetNX(const std::string& key, const std::string& field, const std::string& value) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->hsetnx(key, field, value).first;
    }

    // Set commands implementation
    bool RedisClient::SAdd(const std::string& key, std::vector<std::string>& members) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->sadd(key, members).first;
    }

    redisOPResult RedisClient::SMembers(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->smembers(key);
    }

    bool RedisClient::SRem(const std::string& key, std::vector<std::string>& members) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->srem(key, members).first;
    }

    int RedisClient::SCard(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->scard(key).second;
    }

    bool RedisClient::SIsMember(const std::string& key, const std::string& member) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->sismember(key, member).first;
    }

    redisOPResult RedisClient::SInter(const std::vector<std::string>& keys) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->sinter(keys);
    }

    redisOPResult RedisClient::SUnion(const std::vector<std::string>& keys) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->sunion(keys);
    }

    redisOPResult RedisClient::SDiff(const std::vector<std::string>& keys) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->sdiff(keys);
    }

    redisOPResult RedisClient::SPop(const std::string& key, int count) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->spop(key, count);
    }

    redisOPResult RedisClient::SRandMember(const std::string& key, int count) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->srandmember(key, count);
    }

    // List commands implementation
    bool RedisClient::LPush(const std::string& key, const std::vector<std::string>& members) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->lpush(key, members).first;
    }

    redisOPResult RedisClient::LPop(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->lpop(key);
    }

    redisOPResult RedisClient::LRange(const std::string& key, int start, int end) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->lrange(key, start, end);
    }

    bool RedisClient::RPush(const std::string& key, std::vector<std::string>& members) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->rpush(key, members).first;
    }

    redisOPResult RedisClient::RPop(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->rpop(key);
    }

    int RedisClient::LLen(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->llen(key).second;
    }

    redisOPResult RedisClient::LIndex(const std::string& key, int index) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->lindex(key, index);
    }

    bool RedisClient::LSet(const std::string& key, int index, const std::string& value) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->lset(key, index, value).first;
    }

    int RedisClient::LRem(const std::string& key, int count, const std::string& value) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->lrem(key, count, value).second;
    }

    bool RedisClient::LTrim(const std::string& key, int start, int end) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->ltrim(key, start, end).first;
    }

    redisOPResult RedisClient::RPopLPush(const std::string& source, const std::string& destination) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->rpoplpush(source, destination);
    }

    // Sorted Set commands implementation
    bool RedisClient::ZAdd(const std::string& key, const std::map<std::string, double>& member_scores) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->zadd(key, member_scores).first;
    }

    redisOPResult RedisClient::ZRange(const std::string& key, int start, int stop, bool with_scores) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->zrange(key, start, stop, with_scores);
    }

    redisOPResult RedisClient::ZRevRange(const std::string& key, int start, int stop, bool with_scores) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->zrevrange(key, start, stop, with_scores);
    }

    int RedisClient::ZCard(const std::string& key) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->zcard(key).second;
    }

    double RedisClient::ZScore(const std::string& key, const std::string& member) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->zscore(key, member).second;
    }

    bool RedisClient::ZRem(const std::string& key, const std::vector<std::string>& members) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->zrem(key, members).first;
    }

    // Transaction commands implementation
    bool RedisClient::Multi() {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->multi().first;
    }

    redisOPResult RedisClient::Exec() {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->exec();
    }

    bool RedisClient::Discard() {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->discard().first;
    }

    bool RedisClient::Watch(const std::vector<std::string>& keys) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->watch(keys).first;
    }

    bool RedisClient::Unwatch() {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->unwatch().first;
    }

    // Server commands implementation
    std::string RedisClient::Ping() {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->ping().second;
    }

    redisOPResult RedisClient::Info(const std::string& section) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->info(section);
    }

    bool RedisClient::FlushDB() {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->flushdb().first;
    }

    bool RedisClient::FlushAll() {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->flushall().first;
    }

    // Pub/Sub commands implementation
    int RedisClient::Publish(const std::string& channel, const std::string& message) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->publish(channel, message).second;
    }

    bool RedisClient::Subscribe(const std::vector<std::string>& channels) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->subscribe(channels).first;
    }

    bool RedisClient::Unsubscribe(const std::vector<std::string>& channels) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->unsubscribe(channels).first;
    }

    // Scripting commands implementation
    redisOPResult RedisClient::Eval(const std::string& script, 
                                  const std::vector<std::string>& keys,
                                  const std::vector<std::string>& args) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->eval(script, keys, args);
    }

    redisOPResult RedisClient::EvalSHA(const std::string& sha1,
                                     const std::vector<std::string>& keys,
                                     const std::vector<std::string>& args) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->evalsha(sha1, keys, args);
    }

    std::string RedisClient::ScriptLoad(const std::string& script) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->script_load(script).second;
    }

    redisOPResult RedisClient::ScriptExists(const std::vector<std::string>& sha1s) {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->script_exists(sha1s);
    }

    bool RedisClient::ScriptFlush() {
        auto conn = pool_->acquire();
        releaseOP releaser(pool_, conn);
        return conn->script_flush().first;
    }
}